package com.oig.sys.workflow.schedule;

import cn.hutool.core.collection.CollectionUtil;
import com.oig.common.constants.CommonConstants;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.util.JsonUtil;
import com.oig.sys.msg.vo.NoticeMsgVo;
import com.oig.sys.workflow.constants.JobEnums;
import com.oig.sys.workflow.dto.CronJobLogDto;
import com.oig.sys.workflow.dto.HttpSourceCfgDto;
import com.oig.sys.workflow.msg.IDepNoticeMsgService;
import com.oig.sys.workflow.oauth.IDepOauthService;
import com.oig.sys.workflow.service.ITbCronJobLogService;
import com.oig.sys.workflow.vo.CronJobVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.Date;

@Slf4j
@Component
@RequiredArgsConstructor
public class CronJobHandler {

    private final ITbCronJobLogService jobLogService;
    private final RestTemplate restTemplate;
    private final IDepOauthService depOauthService;
    private final IDepNoticeMsgService depNoticeMsgService;


    private HttpHeaders addHttpHeader(HttpSourceCfgDto sourceCfgDto){
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set(CommonConstants.AUTHORIZATION, CommonConstants.BEARER_TYPE+this.depOauthService.getAccessToken());
        if (CollectionUtil.isNotEmpty(sourceCfgDto.getHeader())) httpHeaders.setAll(sourceCfgDto.getHeader());
        return httpHeaders;
    }

    private HttpEntity<String> addParams(HttpSourceCfgDto sourceCfgDto){
        HttpHeaders httpHeaders = this.addHttpHeader(sourceCfgDto);
        //不开放给用户使用的，开发需要配置好body
        return new HttpEntity<>(sourceCfgDto.getBody(), httpHeaders) ;
    }

    boolean execute(CronJobVo cronJob){
        if (!JobEnums.JobType.HTTP.getCode().equals(cronJob.getJobType())){
            return false;
        }
        HttpSourceCfgDto sourceCfgDto = JsonUtil.toObject(cronJob.getSourceCfg(), HttpSourceCfgDto.class);
        HttpEntity<String> httpEntity = this.addParams(sourceCfgDto);

        boolean excRest = true;
        String executeMsg = "" ;
        ResponseEntity<Object> exchange = null;
        long startTime = System.currentTimeMillis();
        try {
            log.debug("execute:{}",cronJob);
            HttpMethod httpMethod = HttpMethod.POST;
            if (StringUtils.isNotBlank(sourceCfgDto.getMethod())){
                httpMethod = HttpMethod.resolve(sourceCfgDto.getMethod().toUpperCase()) ;
                if (httpMethod==null){
                    throw new BadRequestException("请求参数:sourceCfg配置错误") ;
                }
            }
            exchange = restTemplate.exchange(sourceCfgDto.getUrl(), httpMethod,  httpEntity, Object.class) ;
        } catch (Exception e){
            log.error("error",e);
            excRest = false;
            executeMsg = e.getMessage();
        }
        long endTime = System.currentTimeMillis();
        if (exchange==null||exchange.getStatusCode().isError()) {
            executeMsg = "api返回异常";
            excRest = false;
        }
        CronJobLogDto cronJobLogDto = this.buildLogDto(cronJob, excRest, (int)(endTime - startTime)) ;
        if (excRest){
            Object body = exchange.getBody();
            String jsonStr = body==null?null:JsonUtil.toJSONString(body);
            executeMsg = jsonStr ;
            //返回结果 生成文件？
            log.debug("response:{}",jsonStr);
        }
        this.dealFinishOpts(cronJob, excRest, executeMsg, cronJobLogDto) ;
        cronJobLogDto.setExecuteMsg(executeMsg);
        this.addExeLog(cronJobLogDto);
        return excRest;
    }


    private void dealFinishOpts(CronJobVo cronJob, boolean excRest, String bodyOrMsg, CronJobLogDto cronJobLogDto){
        if (CollectionUtil.isNotEmpty(cronJob.getFinishOpts())){
            cronJob.getFinishOpts().forEach (op->{
                if (JobEnums.FinishOptType.WEB_HOOK.getCode().equals(op) && excRest){       //定时任务调webhook
                    boolean wh = this.dealWebHook(cronJob, bodyOrMsg);
                    if (wh)cronJobLogDto.setWebHookStatus(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
                } else if (JobEnums.FinishOptType.NOTIFY.getCode().equals(op) && !excRest){
                    boolean nm = this.sendNoticeMsg(cronJob, bodyOrMsg);
                    if (nm)cronJobLogDto.setNotifyStatus(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
                }
            });
        }
    }

    private boolean dealWebHook(CronJobVo cronJob, String exRsBody){
        try{
            if (StringUtils.isBlank(cronJob.getWebHook())) return false;
            HttpEntity<String> httpEntity = new HttpEntity<>(exRsBody, null) ;
            this.restTemplate.exchange(cronJob.getWebHook(), HttpMethod.POST, httpEntity, Object.class)  ;
            return true;
        } catch (Exception e){
            log.error(e.getMessage(), e);
        }
        return false;
    }

    private boolean sendNoticeMsg(CronJobVo cronJob, String executeMsg){
        if (cronJob.getPushChannelId()==null) return false;
        //预警消息，定时任务失败通知
        NoticeMsgVo msgVo = this.depNoticeMsgService.addJobNoticeMsg(cronJob, executeMsg);
        return msgVo!=null ;
    }

    private CronJobLogDto buildLogDto(CronJobVo cronJob, boolean result, int duration){
        CronJobLogDto cronJobLogDto = new CronJobLogDto();
        cronJobLogDto.setJobId(cronJob.getId());
        cronJobLogDto.setTenantId(cronJob.getTenantId());
        cronJobLogDto.setTenantName(cronJob.getTenantName());
        cronJobLogDto.setExecuteResult(result?"成功":"失败");
        cronJobLogDto.setExecuteTime(new Date());
        cronJobLogDto.setExecuteDuration(duration);
        cronJobLogDto.setNotifyStatus(CommonEnum.FlagEnum.FALSE_FLAG.getCode());
        cronJobLogDto.setWebHookStatus(CommonEnum.FlagEnum.FALSE_FLAG.getCode());
        return cronJobLogDto ;
    }

    private void addExeLog(CronJobLogDto cronJobLogDto){
        this.jobLogService.addCronJobLog(cronJobLogDto) ;
    }

}
